/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package filter;

import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import javax.ejb.EJB;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import managedbean.LoginAdmin.LoginManagerBean;
import merlion.common.entity.AccessRight;
import merlion.common.entity.SecurityRole;
import merlion.common.entity.Staff;
import merlion.common.session.stateless.SystemUserSessionBeanLocal;

/**
 *
 * @author Zhang Ying
 */
//@WebFilter(filterName = "RestrictedPageFilter", servletNames = {"Faces Servlet"})
public class RestrictedPageFilter implements Filter {

    private static final boolean debug = true;
    // The filter configuration object we are associated with.  If
    // this value is null, this filter instance is not currently
    // configured. 
    private FilterConfig filterConfig = null;
    private String timeoutPage = "logout";
    private String indexPage = "index";
    private String loginPage = "login";
    private String errorPage = "error";
    private String accountActivationPage = "accountActivated";
    private String indexPagePath = "/merlion-war/index.xhtml";
    private String profilePagePath = "http://localhost:8080/merlion-war/Workspace/home.xhtml";  // the home page
    private String denialOfAccessPagePath = "/merlion-war/LoginAdmin/denialOfAccess.xhtml";
    private String loginPagePath = "/merlion-war/LoginAdmin/login.xhtml";
    private String timeoutPagePath = "/merlion-war/LoginAdmin/logout.xhtml";
    private String accountActivationPagePath = "/merlion-war/LoginAdmin/accountActivated.xhtml";
    private String loginAdmin = "LoginAdmin";
    private String commonUtility = "CommonUtility";
    private String workspace = "Workspace";
    @EJB
    private SystemUserSessionBeanLocal loginManager;
    private HttpSession storedSession = null;

    public RestrictedPageFilter() {
    }

    private void doBeforeProcessing(ServletRequest request, ServletResponse response)
            throws IOException, ServletException {
        if (debug) {
            log("RestrictedPageFilter:DoBeforeProcessing");
        }

        // Write code here to process the request and/or response before
        // the rest of the filter chain is invoked.

        // For example, a logging filter might log items on the request object,
        // such as the parameters.
        /*
        for (Enumeration en = request.getParameterNames(); en.hasMoreElements(); ) {
        String name = (String)en.nextElement();
        String values[] = request.getParameterValues(name);
        int n = values.length;
        StringBuffer buf = new StringBuffer();
        buf.append(name);
        buf.append("=");
        for(int i=0; i < n; i++) {
        buf.append(values[i]);
        if (i < n-1)
        buf.append(",");
        }
        log(buf.toString());
        }
         */
    }

    private void doAfterProcessing(ServletRequest request, ServletResponse response)
            throws IOException, ServletException {
        if (debug) {
            log("RestrictedPageFilter:DoAfterProcessing");
        }

        // Write code here to process the request and/or response after
        // the rest of the filter chain is invoked.

        // For example, a logging filter might log the attributes on the
        // request object after the request has been processed. 
        /*
        for (Enumeration en = request.getAttributeNames(); en.hasMoreElements(); ) {
        String name = (String)en.nextElement();
        Object value = request.getAttribute(name);
        log("attribute: " + name + "=" + value.toString());
        
        }
         */

        // For example, a filter might append something to the response.
        /*
        PrintWriter respOut = new PrintWriter(response.getWriter());
        respOut.println("<P><B>This has been appended by an intrusive filter.</B>");
         */
    }

    /**
     *
     * @param request The servlet request we are processing
     * @param response The servlet response we are creating
     * @param chain The filter chain we are processing
     *
     * @exception IOException if an input/output error occurs
     * @exception ServletException if a servlet error occurs
     */
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain)
            throws IOException, ServletException {

        if (debug) {
            log("RestrictedPageFilter:doFilter()");
        }

        doBeforeProcessing(request, response);

        Throwable problem = null;
        try {
            // TODO: remember to uncomment this part:
            if ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse)) {
                HttpServletRequest req = (HttpServletRequest) request;
                HttpServletResponse resp = (HttpServletResponse) response;
                HttpSession session = req.getSession(true);
                String pageRequested = req.getRequestURL().toString();

                Staff currentStaff = (Staff) session.getAttribute(LoginManagerBean.AUTH_KEY);
                
                if (storedSession != null && session.isNew()) {
                    Staff cf = (Staff) storedSession.getAttribute(LoginManagerBean.AUTH_KEY);
                    if (cf != null && currentStaff == null) {
                        //System.out.println("current staff reset!");
                        currentStaff = cf;
                        req.getSession(false).setAttribute(LoginManagerBean.AUTH_KEY, cf);
                    }
                } else {
                    storedSession = session;
                }
                //System.out.println("Current session id : " + session.getId());

                // check for logging in
                if (currentStaff == null
                        && !pageRequested.contains(loginAdmin)
                        && !pageRequested.contains(indexPage)
                        && !pageRequested.contains(timeoutPage)
                        && !pageRequested.contains(accountActivationPage)
                        && !pageRequested.contains("javax")) {
                    //System.out.println("filter: redirect to index page " + pageRequested);
                    resp.sendRedirect(indexPagePath);
                    return;
                }
                // if logged in, direct to profile page if it is entered as index
                if (currentStaff != null
                        && (pageRequested.contains(indexPage) || pageRequested.contains(loginPage))) {
                    resp.sendRedirect(profilePagePath);
                    //System.out.println("filter: redirect to home page");
                    return;
                }
                
                String[] tokens1 = pageRequested.split("/merlion-war/");
                String relativePath = tokens1[1];
                String[] tokens2 = relativePath.split(".xhtml");
                String relativePathName = tokens2[0];
                boolean denialOfAccess = Boolean.FALSE;

                // filter for access rights
                if (!pageRequested.contains(indexPage)
                        && !relativePath.contains(loginAdmin)
                        && !relativePath.contains(workspace)
                        && !relativePath.contains(commonUtility)
                        && !relativePath.toLowerCase().contains(errorPage)
                        && !relativePath.contains("javax")) {
                    Collection<SecurityRole> srs = currentStaff.getSecurityRoles();
                    Collection<AccessRight> ars = new ArrayList<AccessRight>();
                    for (SecurityRole sr : srs) {
                        Collection<AccessRight> acs = sr.getAccessRights();
                        acs.removeAll(ars);
                        ars.addAll(acs);
                    }
                    for (AccessRight ar : ars) {
                        //System.out.println("For debugging:" + ar.getMethodPath());
                        String rp = "/" + relativePathName;
                        if (rp.contains(ar.getMethodPath())) {
                            denialOfAccess = Boolean.TRUE;
                            break;
                        }
                    }
                    if (denialOfAccess == Boolean.FALSE) {
                        //System.out.println("filter: redirect to denial of access");
                        resp.sendRedirect(denialOfAccessPagePath);
                    }
                    //System.out.println("For debugging:" + relativePath);
                    //System.out.println("For debugging:" + relativePathName);
                }


            }
            chain.doFilter(request, response);
        } catch (Throwable t) {
            // If an exception is thrown somewhere down the filter chain,
            // we still want to execute our after processing, and then
            // rethrow the problem after that.
            problem = t;
            t.printStackTrace();
        }

        doAfterProcessing(request, response);

        // If there was a problem, we want to rethrow it if it is
        // a known type, otherwise log it.
        if (problem != null) {
            if (problem instanceof ServletException) {
                throw (ServletException) problem;
            }
            if (problem instanceof IOException) {
                throw (IOException) problem;
            }
            sendProcessingError(problem, response);
        }
    }

    /**
     * Return the filter configuration object for this filter.
     */
    public FilterConfig getFilterConfig() {
        return (this.filterConfig);
    }

    /**
     * Set the filter configuration object for this filter.
     *
     * @param filterConfig The filter configuration object
     */
    public void setFilterConfig(FilterConfig filterConfig) {
        this.filterConfig = filterConfig;
    }

    /**
     * Destroy method for this filter 
     */
    public void destroy() {
    }

    /**
     * Init method for this filter 
     */
    public void init(FilterConfig filterConfig) {
        this.filterConfig = filterConfig;
        if (filterConfig != null) {
            if (debug) {
                log("RestrictedPageFilter:Initializing filter");
            }
        }
    }

    /**
     * Return a String representation of this object.
     */
    @Override
    public String toString() {
        if (filterConfig == null) {
            return ("RestrictedPageFilter()");
        }
        StringBuffer sb = new StringBuffer("RestrictedPageFilter(");
        sb.append(filterConfig);
        sb.append(")");
        return (sb.toString());
    }

    private void sendProcessingError(Throwable t, ServletResponse response) {
        String stackTrace = getStackTrace(t);

        if (stackTrace != null && !stackTrace.equals("")) {
            try {
                response.setContentType("text/html");
                PrintStream ps = new PrintStream(response.getOutputStream());
                PrintWriter pw = new PrintWriter(ps);
                pw.print("<html>\n<head>\n<title>Error</title>\n</head>\n<body>\n"); //NOI18N

                // PENDING! Localize this for next official release
                pw.print("<h1>The resource did not process correctly</h1>\n<pre>\n");
                pw.print(stackTrace);
                pw.print("</pre></body>\n</html>"); //NOI18N
                pw.close();
                ps.close();
                response.getOutputStream().close();
            } catch (Exception ex) {
            }
        } else {
            try {
                PrintStream ps = new PrintStream(response.getOutputStream());
                t.printStackTrace(ps);
                ps.close();
                response.getOutputStream().close();
            } catch (Exception ex) {
            }
        }
    }

    public static String getStackTrace(Throwable t) {
        String stackTrace = null;
        try {
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            t.printStackTrace(pw);
            pw.close();
            sw.close();
            stackTrace = sw.getBuffer().toString();
        } catch (Exception ex) {
        }
        return stackTrace;
    }

    public void log(String msg) {
        filterConfig.getServletContext().log(msg);
    }
}